/* menu.c
 *
 * Triiforce mod by Marc
 *
 * Copyright (c) 2009 The Lemon Man, Nicksasa & WiiPower
 *
 * Distributed under the terms of the GNU General Public License (v2)
 * See http://www.gnu.org/licenses/gpl-2.0.txt for more info.
 *
 *********************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gccore.h>
#include <ogc/lwp_watchdog.h>
#include <wiiuse/wpad.h>
#include <asndlib.h>
#include <network.h> //for network
#include <sys/errno.h>
#include <sys/stat.h> //for mkdir
#include <dirent.h>

#include "tools.h"
#include "lz77.h"
#include "config.h"
#include "patch.h"
#include "codes/codes.h"
#include "codes/patchcode.h"
#include "nand.h"
#include "fat_mine.h"
#include "video.h"
#include "sonido.h"
#include "http.h"
#include "langs.h"
#include "wpad.h"
#include "menu.h"
#include "fat.h"
#include "ios_check.h"

//Menu images
#include "mighty_channels_arrows_png.h"
#include "mighty_channels_background_png.h"
#include "mighty_channels_container_channel_png.h"
#include "mighty_channels_container_channel_wide_png.h"
#include "mighty_channels_empty_png.h"
#include "mighty_channels_loading_png.h"
#include "mighty_channels_numbers_png.h"
#define MAX_TEXTURES	6
#define TEX_ARROWS		0
#define TEX_BACKGROUND	1
#define TEX_CONTAINER	2
#define TEX_EMPTY		3
#define TEX_LOADING		4
#define TEX_NUMBERS		5
static MRCtex* textures[MAX_TEXTURES];

// Constants
#define ARROWS_X		16
#define ARROWS_Y		210
#define ARROWS_WIDTH	20
#define MIGHTY_PATH			"fat:/config/mighty/"
#define IMAGES_PREFIX		"channels"
#define MIGHTY_CONFIG_FILE	"mighty_channels.cfg"
#define MIGHTY_CACHE_FILE	"mighty_cache.dat"
#define MIGHTY_CACHE_VER	0x02

#define MAXGAMES		800
#define MAXPAGES		100
#define EMPTY			-1

#define BLACK	0x000000FF
#define YELLOW	0xFFFF00FF
#define WHITE	0xFFFFFFFF
#define ORANGE	0xeab000ff

#define MAX_TITLE_LEN 32


// Gamelist buffer & variables
typedef struct{
	u64 idInt;
	char id[5];
	char title[MAX_TITLE_LEN+1];

	u32 bannerContent;

	u8 videoMode;
	u8 videoPatch;
	u8 language;
	u8 hooktype;
	u8 ocarina;
	u8 debugger;
	u8 bootMethod;
	u8 datacleartype;

    MRCtex* banner;
} title; //32 bytes!

static title* gameList = NULL;
static u32 gameCnt=0;
static u32 titleCntNand=0; // stfour
static u32 titleCntCache=0 ;

static s16* orden;


// Variables for MIGHTY
static u16 pagina, maxPages=1;
static int selectedGame=0, movingGame=-1;
static const char** lang;
static int loadingAnim=0;
static bool wideScreen=false;
static bool haHabidoCambios=false;
static char tempString[128];
static int nandMode=0;
static bool pageLoaded[MAXPAGES];

extern GXRModeObj *rmode;
extern u32* framebuffer;
extern s32 __IOS_ShutdownSubsystems();
extern void __exception_closeall();

const u8 COLS[]={4, 5};
#define ROWS 2
#define FIRSTCOL 92
#define FIRSTROW 110
const u8 SEPARACIONX[]={152, 112};
#define SEPARACIONY 198
const u8 ANCHOIMAGEN[]={128, 96};
#define ALTOIMAGEN 176

#define ALIGN32(x) (((x) + 31) & ~31)

u8 Video_Mode;

u32 entryPoint;


void*	dolchunkoffset[18];
u32		dolchunksize[18];
u32		dolchunkcount;

void _unstub_start();


typedef void (*entrypoint) (void);

typedef struct _dolheader{
	u32 text_pos[7];
	u32 data_pos[11];
	u32 text_start[7];
	u32 data_start[11];
	u32 text_size[7];
	u32 data_size[11];
	u32 bss_start;
	u32 bss_size;
	u32 entry_point;
} dolheader;


bool __Check_HBC(void){
	u32 *stub = (u32 *)0x80001800;

	// Check HBC stub
	if (*stub)
		return true;
	return false;
}

void __Draw_Loading(void){
 	MRC_Draw_Tile(592,432, textures[TEX_LOADING], 24, loadingAnim);
	MRC_Render_Box(592,432);

	loadingAnim++;
	if(loadingAnim==16)
		loadingAnim=0;
}

void __Draw_Page(int selected){
	int i, j, x, y, containerWidth, game;

	containerWidth=textures[TEX_CONTAINER]->width/2;

	// Background
	MRC_Draw_Texture(0, 0, textures[TEX_BACKGROUND]);

	if(gameCnt==0 || !pageLoaded[pagina]){
		return;
	}

	// Games
	game=COLS[wideScreen]*ROWS*pagina;
	y=FIRSTROW;
	for(i=0; i<ROWS; i++){
		x=FIRSTCOL;
		for(j=0; j<COLS[wideScreen]; j++){
			if(movingGame!=game){
				if(orden[game]==EMPTY){
					MRC_Draw_Texture(x, y, textures[TEX_EMPTY]);
					MRC_Draw_Tile(x, y, textures[TEX_CONTAINER], containerWidth, 0);
				}else if(selected==i*COLS[wideScreen]+j){
					MRC_Draw_Texture(x, y, gameList[orden[game]].banner);
					MRC_Draw_Tile(x, y, textures[TEX_CONTAINER], containerWidth, 1);
				}else{
					MRC_Draw_Texture(x, y, gameList[orden[game]].banner);
					MRC_Draw_Tile(x, y, textures[TEX_CONTAINER], containerWidth, 0);
				}
			}
			game++;
			x+=SEPARACIONX[wideScreen];
		}
		y+=SEPARACIONY;
	}

	// Page number
	x=(pagina+1<10? 300 : 292);
	sprintf(tempString, "%d", pagina+1);
	for(i=0; i<strlen(tempString); i++){
		MRC_Draw_Tile(x, 404, textures[TEX_NUMBERS], 8, tempString[i]-48);
		x+=8;
	}
	MRC_Draw_Tile(x, 404, textures[TEX_NUMBERS], 8, 10);
	x+=10;
	sprintf(tempString, "%d", maxPages);
	for(i=0; i<strlen(tempString); i++){
		MRC_Draw_Tile(x, 404, textures[TEX_NUMBERS], 8, tempString[i]-37);
		x+=8;
	}
	//MRC_Draw_String(300, 404, 0x808080FF, tempString);
	//MRC_Draw_String(310, 404, 0xd0d0d0FF, tempString);
	//sprintf(tempString, "hotspot=%d", selected);
	//MRC_Draw_String(40, 444, 0xFFFF00FF, tempString);

	if(movingGame>-1){
		if(orden[movingGame]==EMPTY){
			MRC_Draw_Texture(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), textures[TEX_EMPTY]);
		}else{
			MRC_Draw_Texture(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), gameList[orden[movingGame]].banner);
		}
	}

	// Arrows
	MRC_Draw_Tile(ARROWS_X, ARROWS_Y, textures[TEX_ARROWS], ARROWS_WIDTH, 0+(pagina>0)+(pagina>0 && selected==HOTSPOT_LEFT));
	MRC_Draw_Tile(640-ARROWS_X, ARROWS_Y, textures[TEX_ARROWS], ARROWS_WIDTH, 3+(pagina+1<maxPages)+(pagina+1<maxPages && selected==HOTSPOT_RIGHT));
}

void __Draw_Button(int hot, const char* text, bool selected){
	hotSpot button=Wpad_GetHotSpotInfo(hot);
	int textX=button.x+(button.width-strlen(text)*8)/2;
	u32 color;
	if(selected){
		MRC_Draw_Box(button.x, button.y, button.width, button.height/2, 0x3c7291ff);
		MRC_Draw_Box(button.x, button.y+button.height/2, button.width, button.height/2, 0x2d6483ff);
		color=WHITE;
	}else{
		MRC_Draw_Box(button.x, button.y, button.width, button.height/2, 0xe3e3e3ff);
		MRC_Draw_Box(button.x, button.y+button.height/2, button.width, button.height/2, 0xd8d8d8ff);
		MRC_Draw_Box(button.x, button.y, button.width, 1, 0xb6b4c5ff);
		MRC_Draw_Box(button.x, button.y+button.height-1, button.width, 1, 0xb6b4c5ff);
		MRC_Draw_Box(button.x, button.y, 1, button.height, 0xb6b4c5ff);
		MRC_Draw_Box(button.x+button.width-1, button.y, 1, button.height, 0xb6b4c5ff);
		color=0x404040ff;
	}
	MRC_Draw_String(textX, button.y+button.height/2-8, color, text);
}

void __Draw_Window(int width, int height, const char* title){
	int x=(640-width)/2;
	int y=(480-height)/2-32;
	__Draw_Page(-1);
	MRC_Draw_Box(0, 0, 640, 480, 0x000000c0);

	MRC_Draw_Box(x, y, width, 32, ORANGE-0x20);
	MRC_Draw_Box(x, y+32, width, height, WHITE-0x20);

	MRC_Draw_String(x+(width-strlen(title)*8)/2, y+8, WHITE, title);
}

void __Draw_Message(const char* title, int ret){
	int i;

	MRC_Draw_Texture(0, 0, textures[TEX_BACKGROUND]);
	MRC_Draw_Box(0, 200, 640, 48, WHITE-0x20);
	for(i=0;i<16;i++){
		MRC_Draw_Box(0, 200-16+i, 640, 1, i*2);
		MRC_Draw_Box(0, 200+48+16-i, 640, 1, i*2);
	}
	MRC_Draw_String((640-strlen(title)*8)/2, 216, BLACK, title);

	if(ret<0){
		sprintf(tempString, "ret=%d", ret);
		MRC_Draw_String(540, 216, 0xff0000ff, tempString);
	}

	MRC_Render_Screen();
	
	if(ret!=0)
		Wpad_WaitButtons();
}
#define QUESTION_BUTTON_X			100
#define QUESTION_BUTTON_Y			240
#define QUESTION_BUTTON_SEPARATION	32
#define QUESTION_BUTTON_WIDTH		200
#define QUESTION_BUTTON_HEIGHT		48
bool __Question_Window(const char* title, const char* text, const char* a1, const char* a2){
	int i, hotSpot, hotSpotPrev;
	bool ret=false, repaint=true;

	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<2; i++)
		Wpad_AddHotSpot(i,
			QUESTION_BUTTON_X+i*(QUESTION_BUTTON_WIDTH+QUESTION_BUTTON_SEPARATION),
			QUESTION_BUTTON_Y,
			QUESTION_BUTTON_WIDTH,
			QUESTION_BUTTON_HEIGHT,
			i, i,
			!i, !i
		);


	__Draw_Window(512, 128, title);
	MRC_Draw_String(100, 200, BLACK, text);

	// Loop
	hotSpot=hotSpotPrev=-1;

	ret=MENU_SELECT_GAME;
	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if((hotSpot!=hotSpotPrev && hotSpot<2) || repaint){
			hotSpotPrev=hotSpot;

			__Draw_Button(0, a1, hotSpot==0);
			__Draw_Button(1, a2, hotSpot==1);

			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)) && hotSpot!=-1){
			if(hotSpot==1)
				ret=true;
			break;
		}
	}

	return ret;
}


void calcularPaginas(void){
	int i;
	maxPages=0;
	for(i=MAXGAMES-1; i>-1; i--){
		if(orden[i]!=EMPTY){
			break;
		}
	}

	while(maxPages*COLS[wideScreen]*ROWS<=i){
		maxPages++;
	}

	if(movingGame!=-1)
		maxPages++;

	if(maxPages>MAXPAGES)
		maxPages=MAXPAGES;
}

u8 *ISFS_GetFile(u8 *path, u32 *size, s32 length)
{
        *size = 0;
        
        s32 fd = ISFS_Open((const char *) path, ISFS_OPEN_READ);
        u8 *buf = NULL;
        static fstats stats ATTRIBUTE_ALIGN(32);
        
        if (fd >= 0)
        {
                if (ISFS_GetFileStats(fd, &stats) >= 0)
                {
                        if (length <= 0) length = stats.file_length;
                        if (length > 0)
                                buf = (u8 *) memalign(32, ALIGN32(length));

                        if (buf)
                        {
                                *size = stats.file_length;
                                if (ISFS_Read(fd, (char*)buf, length) != length)
                                {
                                        *size = 0;
                                        free(buf);
                                }
                        }
                }
                ISFS_Close(fd);
        }

        if (*size > 0)
        {
                DCFlushRange(buf, *size);
                ICInvalidateRange(buf, *size);
        }
        
        return buf;
}


bool FindDol(u64 titleid, char *out, u16 bootcontent)
{
        u32 high = TITLE_UPPER(titleid);
        u32 low = TITLE_LOWER(titleid);

        u8 check[6] = {0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
 
        char path[ISFS_MAXPATH + 1];
        sprintf(path, "/title/%08x/%08x/content", high, low);
        
        u32 Countall = 0;
        if(ISFS_ReadDir(path, NULL, &Countall) != ISFS_OK)
        {
                printf("Error: could not get dir entry count!\n");
                return false;
        }

        size_t bufferSize = ((12 + 1) * Countall) + 1;
        char *namesBuffer = (char *)memalign(32, ALIGN32(bufferSize));
        char *holder = namesBuffer;
        if(namesBuffer == NULL)
        {
                printf("ERROR: could not allocate buffer for name list!\n");
                return false;
        }

        if(ISFS_ReadDir(path, namesBuffer, &Countall) != ISFS_OK)
        {
                printf("Reading content folder of the title failed!\n");
                free(holder);
                return false;
        }
        DCFlushRange(namesBuffer, bufferSize);
        ICInvalidateRange(namesBuffer, bufferSize);

        while (strlen(namesBuffer) != 0)
        {
                if (strcasestr(namesBuffer, ".app") != NULL && strtoul(namesBuffer, NULL, 16) != bootcontent)
                {       
                        sprintf(path, "/title/%08x/%08x/content/%s", high, low, namesBuffer);
                        printf("Checking --> %s\n", path);

                        u32 size = 0;
                        u32 *data = (u32 *) ISFS_GetFile((u8 *) &path, &size, 6);
                        if (data != NULL && size == 6)
                        {
        
                                if (isLZ77compressed((u8*)data))
                                {
                                        printf("Found LZ77 %s compressed content --> %s\n", data[0] == 0x10 ? "0x10" : "0x11", namesBuffer);
                                        printf("This is most likely the main DOL, decompressing for checking\n");
                                        
                                        u32 *compressed = (u32 *) ISFS_GetFile((u8 *) &path, &size, -1);
                                        if (compressed != NULL)
                                        {
                                                u8 *decompressed;
                                                u32 decomp_size = 0;
                                                if (decompressLZ77content((u8 *)compressed, 32, &decompressed, &decomp_size, 0) < 0)
                                                {
                                                        printf("Decompressing file...Failed!\n");
                                                        free(compressed);
                                                        continue;
                                                }
                                                memcpy(data, decompressed, 6);
                                                free(decompressed);
                                                free(compressed);
                                        }
                                }
                        
                                if(memcmp(data, check, 6) == 0)
                                {
                                        printf("Found DOL --> %s\n", namesBuffer);
                                        sprintf(out, "%s", path);
                                        free(holder);      
                                        free(data);
                                        return true;
                                } 
                               free(data);
                        }
                }
                namesBuffer += strlen(namesBuffer) + 1;
        }
                
        free(holder);      
        
        printf("No .dol found\n");
        return false;
}

void sneek_video_patch(void *addr, u32 len)
{
u8 *addr_start = addr;
u8 *addr_end = addr+len;
while(addr_start < addr_end)
{
if( *(vu32*)(addr_start) == 0x3C608000 )
{
if( ((*(vu32*)(addr_start+4) & 0xFC1FFFFF ) == 0x800300CC) && ((*(vu32*)(addr_start+8) >> 24) == 0x54 ) )
{
//dbgprintf("DIP:[patcher] Found VI pattern:%08X\n", (u32)(addr_start) | 0x80000000 );
*(vu32*)(addr_start+4) = 0x5400F0BE | ((*(vu32*)(addr_start+4) & 0x3E00000) >> 5	);
}
}
addr_start +=4;
}
}


void patch_dol(bool bootcontent){
	s32 ret;
	(void)ret; // Silence a warning
	int i;
	bool hookpatched = false;
	
	for (i=0;i < dolchunkcount;i++)	{		
		if (!bootcontent){
			if (languageoption != -1){
			ret = patch_language(dolchunkoffset[i], dolchunksize[i], languageoption);
			}
            search_video_modes(dolchunkoffset[i], dolchunksize[i]);
			switch(videopatchoption)
			{
			case 1:
			sneek_video_patch(dolchunkoffset[i], dolchunksize[i]);
			break;
			case 2:
			patch_video_modes_to(rmode, 1);
			break;
			case 3:
			patch_video_modes_to(rmode, 2);
			break;
			case 4:
			patch_video_modes_to(rmode, 3);
			case 5:
			patch_video_modes_to(rmode, 3);
			sneek_video_patch(dolchunkoffset[i], dolchunksize[i]);
			break;
			case 0:
			default:
			break;
		
		
		}
		}
        return_to_channel();

		if (hooktypeoption != 0){
			// Before this can be done, the codehandler needs to be in memory, and the code to patch needs to be in the right pace
			if (dochannelhooks(dolchunkoffset[i], dolchunksize[i], bootcontent)){
				hookpatched = true;
			}			
		}
	}
	if (hooktypeoption != 0 && !hookpatched){
		printf("Error: Could not patch the hook\n");
		printf("Ocarina and debugger won't work\n");
		sleep(5);
	}
}  


u32 load_dol(u8 *buffer){
	dolchunkcount = 0;
	
	dolheader *dolfile;
	dolfile = (dolheader *)buffer;
	
	printf("Entrypoint: %08x\n", dolfile->entry_point);
	printf("BSS: %08x, size = %08x(%u)\n", dolfile->bss_start, dolfile->bss_size, dolfile->bss_size);

	memset((void *)dolfile->bss_start, 0, dolfile->bss_size);
	DCFlushRange((void *)dolfile->bss_start, dolfile->bss_size);
        ICInvalidateRange((void *)dolfile->bss_start, dolfile->bss_size);
	
    printf("BSS cleared\n");

	int i;
	for (i = 0; i < 7; i++){	
	if ((!dolfile->text_size[i]) || (dolfile->text_start[i] < 0x100)) continue;
		if(dolfile->text_pos[i] < sizeof(dolheader))
			continue;
	    
		dolchunkoffset[dolchunkcount] = (void *)dolfile->text_start[i];
		dolchunksize[dolchunkcount] = dolfile->text_size[i];
        
		//printf("Moving text section %u from offset %08x to %08x-%08x...\n", i, dolfile->text_pos[i], dolchunkoffset[dolchunkcount], dolchunkoffset[dolchunkcount]+dolchunksize[dolchunkcount]);
		
		memmove (dolchunkoffset[dolchunkcount], (void *)buffer + dolfile->text_pos[i], dolchunksize[dolchunkcount]);
		DCFlushRange (dolchunkoffset[dolchunkcount], dolchunksize[dolchunkcount]);
		ICInvalidateRange (dolchunkoffset[dolchunkcount],dolchunksize[dolchunkcount]);
		dolchunkcount++;
		//printf("done\n");
		//fflush(stdout);			
	}

	for(i = 0; i < 11; i++){
	if ((!dolfile->data_size[i]) || (dolfile->data_start[i] < 0x100)) continue;
		if(dolfile->data_pos[i] < sizeof(dolheader))
			continue;
		
		dolchunkoffset[dolchunkcount] = (void *)dolfile->data_start[i];
		dolchunksize[dolchunkcount] = dolfile->data_size[i];
		
		//printf("done\n");
		//fflush(stdout);

    //printf("Moving data section %u from offset %08x to %08x-%08x...\n", i, dolfile->data_pos[i], dolchunkoffset[dolchunkcount], dolchunkoffset[dolchunkcount]+dolchunksize[dolchunkcount]);
    memmove (dolchunkoffset[dolchunkcount], (void *)buffer + dolfile->data_pos[i], dolchunksize[dolchunkcount]);	
    DCFlushRange (dolchunkoffset[dolchunkcount], dolchunksize[dolchunkcount]);
	ICInvalidateRange (dolchunkoffset[dolchunkcount],dolchunksize[dolchunkcount]);
    dolchunkcount++;	
	} 
	return dolfile->entry_point;
}


s32 search_and_read_dol(u64 titleid, u8 **contentBuf, u32 *contentSize, bool skip_bootcontent){
	char filepath[ISFS_MAXPATH] ATTRIBUTE_ALIGN(32);
	int ret;
	u16 bootindex;
	u16 bootcontent;
	bool bootcontent_loaded;
	
	u8 *tmdBuffer = NULL;
	u32 tmdSize;
	tmd_content *p_cr = NULL;

// Opening the tmd only to get to know which content is the apploader
	printf("Reading TMD...");

	sprintf(filepath, "/title/%08x/%08x/content/title.tmd", TITLE_UPPER(titleid), TITLE_LOWER(titleid));
	ret = read_full_file_from_nand(filepath, &tmdBuffer, &tmdSize);
	if (ret < 0)
	{
		printf("Reading TMD failed\n");
		return ret;
	}
	printf("done\n");
	
	bootindex = ((tmd *)SIGNATURE_PAYLOAD((signed_blob *)tmdBuffer))->boot_index;
	p_cr = TMD_CONTENTS(((tmd *)SIGNATURE_PAYLOAD((signed_blob *)tmdBuffer)));
	bootcontent = p_cr[bootindex].cid;

	free(tmdBuffer);

	// Write bootcontent to filepath and overwrite it in case another .dol is found
	sprintf(filepath, "/title/%08x/%08x/content/%08x.app", TITLE_UPPER(titleid), TITLE_LOWER(titleid), bootcontent);

	if (skip_bootcontent)
	{
		bootcontent_loaded = false;
		printf("Searching for main DOL...\n");
			
		ret = FindDol(titleid, filepath, bootcontent);
		if (ret < 0)
		{
			printf("Searching for main.dol failed\n");
			printf("Press any button to load NAND loader instead...\n");
			Wpad_WaitButtons();
			bootcontent_loaded = true;
		}
	} else
	{
		bootcontent_loaded = true;
	}
	
    printf("Loading DOL: %s\n", filepath);
	
	ret = read_full_file_from_nand(filepath, contentBuf, contentSize);
	if (ret < 0)
	{
		printf("Reading .dol failed\n");
		return ret;
	}
	
	if (isLZ77compressed(*contentBuf))
	{
		u8 *decompressed;
		ret = decompressLZ77content(*contentBuf, *contentSize, &decompressed, contentSize,0);
		if (ret < 0)
		{
			printf("Decompression failed\n");
			free(*contentBuf);
			return ret;
		}
		free(*contentBuf);
		*contentBuf = decompressed;
	}	
	
	if(bootcontent_loaded){
		return 1;
	}else{
		return 0;
	}
}


void determineVideoMode(u64 titleid){
	if (videooption == 0){		
		// Get rmode and Video_Mode for system settings first
		u32 tvmode = CONF_GetVideo();

		// Attention: This returns &TVNtsc480Prog for all progressive video modes
		rmode = VIDEO_GetPreferredMode(0);
		
		switch (tvmode) 
		{
			case CONF_VIDEO_PAL:
				if (CONF_GetEuRGB60() > 0) 
				{
					Video_Mode = VI_EURGB60;
				}
				else 
				{
					Video_Mode = VI_PAL;
				}
				break;

			case CONF_VIDEO_MPAL:
				Video_Mode = VI_MPAL;
				break;

			case CONF_VIDEO_NTSC:
			default:
				Video_Mode = VI_NTSC;
				
		}

		// Overwrite rmode and Video_Mode when Default Video Mode is selected and Wii region doesn't match the channel region
		u32 low;
		low = TITLE_LOWER(titleid);
		char Region = low % 256;
		if (*(char *)&low != 'W') // Don't overwrite video mode for WiiWare
		{
			switch (Region) 
			{
				case 'P':
				case 'D':
				case 'F':
				case 'X':
				case 'Y':
					if (CONF_GetVideo() != CONF_VIDEO_PAL)
					{
						Video_Mode = VI_EURGB60;

						if (CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable())
						{
							rmode = &TVNtsc480Prog; // This seems to be correct!
						}
						else
						{
							rmode = &TVEurgb60Hz480IntDf;
						}				
					}
					break;

				case 'E':
				case 'J':
				case 'T':
					if (CONF_GetVideo() != CONF_VIDEO_NTSC)
					{
						Video_Mode = VI_NTSC;
						if (CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable())
						{
							rmode = &TVNtsc480Prog;
						}
						else
						{
							rmode = &TVNtsc480IntDf;
						}				
					}
			}
		}
	}else{
		if (videooption == 1){
			rmode = &TVNtsc480IntDf;
		} else if (videooption == 2){
			rmode = &TVNtsc480Prog;
		} else if (videooption == 3){
			rmode = &TVEurgb60Hz480IntDf;
		} else if (videooption == 4){
			rmode = &TVEurgb60Hz480Prog;
		} else if (videooption == 5){
			rmode = &TVPal528IntDf;
		} else if (videooption == 6){
			rmode = &TVMpal480IntDf;
		} else if (videooption == 7){
			rmode = &TVMpal480Prog;
		}
		Video_Mode = (rmode->viTVMode) >> 2;
	}
}

void setVideoMode(){	
	*(u32 *)0x800000CC = Video_Mode;
	DCFlushRange((void*)0x800000CC, sizeof(u32));
	
	// Overwrite all progressive video modes as they are broken in libogc
	if (videomode_interlaced(rmode) == 0){
		rmode = &TVNtsc480Prog;
	}

	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(framebuffer);
	VIDEO_SetBlack(FALSE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	
	if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
}

#define MAXAPPLOADERGAMES 7
static const char games[MAXAPPLOADERGAMES][3]={
	"WAL",	//ArtSyle: light trax
	"WDH",	//ArtSyle: Rotohex
	"WOB",	//ArtSyle: ORBIENT
	"WPR",	//ArtSyle: CUBELLO
	"WA8",	//ArtSyle: Penta Tentacles
	"WB7",	//Midnight Pool
	"WSP"	//Pokemon Rumble
};

bool checkApploaderGame(char *id){
	u32 i;

	for(i = 0; i < MAXAPPLOADERGAMES; i++)
	{
        if(memcmp(id, &games[i], 3) == 0)
		//if(games[i][0]==id[0] && games[i][1]==id[1] && games[i][2]==id[2]){
		return true;
		
	}
	return false;
}

int __CreateNullFile(const char* path, const char* fileName, int fileSize){
	int i;
	unsigned char* nullData;
	sprintf(tempString, "%s/%s", path, fileName);
	if(!Fat_CheckFile(tempString)){
		nullData=allocate_memory(fileSize);
		for(i=0;i<fileSize;i++)
			nullData[i]=0;
		Fat_SaveFile(tempString, (void *)&nullData, fileSize);
		free(nullData);
		return 1;
	}

	return 0;
}
void killdir(char* path) {

	DIR* pdir = opendir(path);

	if (pdir != NULL) {

		while(true) {
			struct dirent* pent = readdir(pdir);
			if(pent == NULL) break;
			
			if(strcmp(".", pent->d_name) != 0 && strcmp("..", pent->d_name) != 0) {
				char dnbuf[260];
				sprintf(dnbuf, "%s/%s", path, pent->d_name);
				
				struct stat statbuf;
				stat(dnbuf, &statbuf);
				
				if(S_ISDIR(statbuf.st_mode)) {
					killdir(dnbuf);
					unlink(dnbuf);
				} else {
					unlink(dnbuf);
				}
			}
		}
		
		closedir(pdir);
	} 
}


int checkNeededSave(title* game){
	int ret=0;
	char dataPath[128];
	sprintf(dataPath, "fat:/%s/title/00010001/%08x/data", Get_Path(), TITLE_LOWER(game->idInt));

	if (datacleartypeoption)
		killdir(dataPath);

	if(memcmp(game->id, "WTR", 3)==0 || memcmp(game->id, "W8C", 3)==0 || memcmp(game->id, "WVB", 3)==0){ //Bit Trip BEAT/CORE/VOID
		Fat_MakeDir(dataPath);
		ret+=__CreateNullFile(dataPath, "banner.bin", 61600);
		ret+=__CreateNullFile(dataPath, "savefile.dat", 512);
	}else if(memcmp(game->id, "WRU", 3)==0){ //Bit Trip RUNNER
		Fat_MakeDir(dataPath);
		ret+=__CreateNullFile(dataPath, "banner.bin", 61600);
		ret+=__CreateNullFile(dataPath, "savefile.dat", 1024);
	}else if(memcmp(game->id, "WBF", 3)==0){ //Bit Trip FATE
		Fat_MakeDir(dataPath);
		ret+=__CreateNullFile(dataPath, "banner.bin", 61600);
		ret+=__CreateNullFile(dataPath, "savefile.dat", 192);
	}else if(memcmp(game->id, "WTF", 3)==0){ //Bit Trip FLUX
		Fat_MakeDir(dataPath);
		ret+=__CreateNullFile(dataPath, "banner.bin", 61600);
		ret+=__CreateNullFile(dataPath, "savefile.dat", 160);
	}

	return ret;
}



void __Start_Game(void){
	entrypoint appJump;
	int ret;
	u32 requested_ios;
	u8 *dolbuffer;
	u32 dolsize;
	bool bootcontentloaded;
	
	
	//if (ES_GetTitleID(&old_title_id) < 0)
	//{
	//old_title_id = (0x00010001ULL << 32) | *(u32 *)0x80000000; //test
	//}


	u64 titleid=gameList[orden[selectedGame]].idInt;
	videooption=gameList[orden[selectedGame]].videoMode;
	videopatchoption=gameList[orden[selectedGame]].videoPatch;
	languageoption=gameList[orden[selectedGame]].language-1;
	hooktypeoption=gameList[orden[selectedGame]].hooktype;
	ocarinaoption=gameList[orden[selectedGame]].ocarina;
	debuggeroption=gameList[orden[selectedGame]].debugger;			
	bootmethodoption=gameList[orden[selectedGame]].bootMethod;
	datacleartypeoption=gameList[orden[selectedGame]].datacleartype;
	free(orden);
	free(gameList);

	#ifndef DEBUG_MODE
	Con_Init(CONSOLE_X, CONSOLE_Y, CONSOLE_WIDTH, CONSOLE_HEIGHT);
	#endif
	printf("Now loading game...\n\n");
	

	if(nandMode!=REAL_NAND){
		ret=checkNeededSave(&gameList[orden[selectedGame]]);
		/*if(ret>0){
			printf("NOTE: A savegame was created for this game.\n");
			printf("%d needed files were created.\n", ret);
			printf(lang[7]);
			Wpad_WaitButtons();
		}*/
	}
	
	//Unmount FAT
	Fat_Unmount();

	//Initialize NAND
	if(nandMode!=REAL_NAND){
		ret=Enable_Emu(nandMode);
		if(ret<0){
			printf("ERROR: I can't enable NAND emulator (ret=%d).\n", ret);
		}
	}

	ISFS_Initialize();	

	ret = search_and_read_dol(titleid, &dolbuffer, &dolsize, (bootmethodoption == 0));
	if (ret < 0){
		printf(".dol loading failed\n");
		return;
	}
	bootcontentloaded = (ret == 1);

	determineVideoMode(titleid);
	
	entryPoint = load_dol(dolbuffer);
	
	free(dolbuffer);

	printf(".dol loaded\n");

	ret = identify(titleid, &requested_ios);
	if (ret < 0)
	{
		printf("Identify failed\n");
		return;
	}
	
    ISFS_Deinitialize();
	
	// Set the clock
	settime(secs_to_ticks(time(NULL) - 946684800));

// Memory setup when booting the main.dol
	if (entryPoint != 0x3400){
*(u32 *)0x80000034 = 0;			// Arena High, the apploader does this too
*(u32 *)0x800000F4 = 0x817FE000;	// BI2, the apploader does this too
*(u32 *)0x800000F8 = 0x0E7BE2C0;	// bus speed
*(u32 *)0x800000FC = 0x2B73A840;	// cpu speed
DCFlushRange((void*)0x80000000, 0x100);
memset((void *)0x817FE000, 0, 0x2000); // Clearing BI2, or should this be read from somewhere?
DCFlushRange((void*)0x817FE000, 0x2000);
if (hooktypeoption == 0)
{*(u32 *)0x80003180 = TITLE_LOWER(titleid);}
else
{*(u32 *)0x80003180 = 0;}		// No comment required here
*(u32 *)0x80003184 = 0x81000000;	// Game id address, while there's all 0s at 0x81000000 when using the apploader...
DCFlushRange((void*)0x80003180, 32);
}
	
	// Remove 002 error
    printf("Fake IOS Version(%u)\n", requested_ios);
	*(u16 *)0x80003140 = requested_ios;
	*(u16 *)0x80003142 = 0xffff;
	*(u16 *)0x80003188 = requested_ios;
	*(u16 *)0x8000318A = 0xffff;

	DCFlushRange((void*)0x80003140, 4);
	DCFlushRange((void*)0x80003188, 4);


	/*ret = ES_SetUID(titleid);
	if (ret < 0){
		printf("ES_SetUID failed %d", ret);
		return;
	}*/
	printf("ES_SetUID successful\n");
	
	
	if(hooktypeoption != 0){
		do_codes(titleid);
	}
	
	patch_dol(bootcontentloaded);

	printf("Loading complete, booting...\n");

	appJump = (entrypoint)entryPoint;

	//sleep(5);

	setVideoMode();

	//---GREENSCREEN FIX---
	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(framebuffer);
	VIDEO_SetBlack(TRUE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	//---------------------

	WPAD_Shutdown();
	
	IRQ_Disable();
	__IOS_ShutdownSubsystems();
	__exception_closeall();
	
	//SYS_ResetSystem(SYS_SHUTDOWN, 0, 0);

	if (entryPoint != 0x3400)
	{
		if (hooktypeoption != 0)
		{
			__asm__(
						"lis %r3, entryPoint@h\n"
						"ori %r3, %r3, entryPoint@l\n"
						"lwz %r3, 0(%r3)\n"
						"mtlr %r3\n"
						"lis %r3, 0x8000\n"
						"ori %r3, %r3, 0x18A8\n"
						"mtctr %r3\n"
						"bctr\n"
						);
						
		} else
		{
			appJump();	
		}
	} else
	{
		if (hooktypeoption != 0)
		{
			__asm__(
						"lis %r3, returnpoint@h\n"
						"ori %r3, %r3, returnpoint@l\n"
						"mtlr %r3\n"
						"lis %r3, 0x8000\n"
						"ori %r3, %r3, 0x18A8\n"
						"mtctr %r3\n"
						"bctr\n"
						"returnpoint:\n"
						"bl DCDisable\n"
						"bl ICDisable\n"
						"li %r3, 0\n"
						"mtsrr1 %r3\n"
						"lis %r4, entryPoint@h\n"
						"ori %r4,%r4,entryPoint@l\n"
						"lwz %r4, 0(%r4)\n"
						"mtsrr0 %r4\n"
						"rfi\n"
						);
		}else{
			_unstub_start();
		}
	}
}

void __Save_Changes(void){
	int i, j, configsize;
	unsigned char *outBuffer;

	configsize=1+15+gameCnt*(2+4+7+3); //1=cfgversion + 15=reserved   +   (2=position   4=id   7=config   3=reserved)=16
	#ifdef DEBUG_MODE
	printf("Saving changes... (%d bytes)\n", configsize);
	#endif
	outBuffer=allocate_memory(configsize);

	for(i=0; i<16; i++)
		outBuffer[i]=0;
	outBuffer[0]=MIGHTY_VERSION;

	j=16;
	for(i=0; i<MAXGAMES; i++){
		__Draw_Loading();
		if(orden[i]!=EMPTY){
			outBuffer[j]=i%256;
			outBuffer[j+1]=i/256;
			outBuffer[j+2]=gameList[orden[i]].id[0];
			outBuffer[j+3]=gameList[orden[i]].id[1];
			outBuffer[j+4]=gameList[orden[i]].id[2];
			outBuffer[j+5]=gameList[orden[i]].id[3];

			outBuffer[j+6]=gameList[orden[i]].videoMode;
			outBuffer[j+7]=gameList[orden[i]].videoPatch;
			outBuffer[j+8]=gameList[orden[i]].language;
			outBuffer[j+9]=gameList[orden[i]].hooktype;
			outBuffer[j+10]=gameList[orden[i]].ocarina;
			outBuffer[j+11]=gameList[orden[i]].debugger;
			outBuffer[j+12]=gameList[orden[i]].bootMethod;
			outBuffer[j+13]=gameList[orden[i]].datacleartype;
			outBuffer[j+14]=0;
			outBuffer[j+15]=0;
			j+=16;
		}
	}

	// Save changes to FAT
	Fat_SaveFile(MIGHTY_PATH MIGHTY_CONFIG_FILE, (void *)&outBuffer, configsize);

	haHabidoCambios=false;
}

// --- stfour --- IS WORKING HERE

int __WriteCache (void)
	{
	int i;
	s32 ret;
	(void)ret; // Silence warning
	unsigned char id = MIGHTY_CACHE_VER;

	FILE *fd = fopen(MIGHTY_PATH MIGHTY_CACHE_FILE, "wb");
		
	if (!fd) return -1;
	
	// write count, titleCntNand is used to refresh cache if needed
	
	ret = fwrite (&id, 1, sizeof (id), fd);
	ret = fwrite (&titleCntNand, 1, sizeof (titleCntNand), fd);
	ret = fwrite (&gameCnt, 1, sizeof (gameCnt), fd);
	
	// write games data
	for (i = 0; i < gameCnt; i++)
		{
    ret = fwrite (&gameList[i], 1, sizeof (title) - sizeof(MRCtex*), fd); // Do not save texture pointer... little save space
		}
	
	fclose(fd);
	
	return 1;
	}

int __ReadCache (void)
	{
	int i;
	s32 ret;
	unsigned char id;
	
	FILE *fd = NULL;

	fd = fopen(MIGHTY_PATH MIGHTY_CACHE_FILE, "rb");
		
	if (!fd) return -1;

	// read the cache id... if cache version id is lower than actual the cache is no more valid
	ret = fread (&id, 1, sizeof (id), fd);
	if (id < MIGHTY_CACHE_VER)
		{
		fclose (fd);
		return -1;
		}
	
	// read and lastTitle count written, if it is changend, cache must be rebuilt
	ret = fread (&titleCntCache, 1, sizeof (titleCntCache), fd);
	ret = fread (&gameCnt, 1, sizeof (gameCnt), fd);
	
	if (!ret || gameCnt == 0)
		{
		fclose (fd);
		return -1;
		}
	
	gameList=allocate_memory(sizeof(title)*gameCnt);
	
	// write games data
	for (i = 0; i < gameCnt; i++)
		{
		ret = fread (&gameList[i], 1, sizeof (title) - sizeof(MRCtex*), fd); // Do not save texture pointer... little save space
		gameList[i].banner = NULL; // Maybe this is not needed
		}
		
	fclose (fd);

	return gameCnt;
	}

int __Get_Entries(void){
	int ret, i, j;

	__Draw_Message(lang[22], 0);
	
	u32 maxnum;
	dirent_t *list = NULL;
	char path[ISFS_MAXPATH] ATTRIBUTE_ALIGN(32);
	sprintf(path, "/title/00010001");

    ret = getdir(path, &list, &maxnum);
    if (ret < 0)
		{
		printf("Reading folder %s failed\n", path);

		return ret;
		}
	
	titleCntNand = maxnum;

	//------------------------------------------------------------------------------------------------
	// stfour: Check if cache is available and aligned
	if (titleCntNand == titleCntCache) 
		{
		free (list);
		return gameCnt;
		}

	//------------------------------------------------------------------------------------------------
	// stfour: Check failed, rebuild it...
	if (gameList != NULL) free (gameList);

	
	j=0;
	for(i=0; i<maxnum; i++){
		// Ignore channels starting with H (Channels) and U (Loadstructor channels)
		// Also ignore the HBC, title id "JODI"
		if (memcmp(list[i].name, "48", 2) == 0 || memcmp(list[i].name, "55", 2) == 0 || memcmp(list[i].name, "4a4f4449", 8) == 0 || memcmp(list[i].name, "4A4F4449", 8) == 0){
		j++;
		}
	}
	

	gameList=allocate_memory(sizeof(title)*(maxnum-j));
	if(gameList==NULL){
		free(list);
		printf("Out of memory\n");
		return -1;
	}
	gameCnt=0;


	for(i=0; i<maxnum; i++){
		// Ignore channels starting with H (Channels) and U (Loadstructor channels)
if (memcmp(list[i].name, "48", 2) != 0 && memcmp(list[i].name, "55", 2) != 0
		// Also ignore the HBC, title id "JODI" and 0xaf1bf516
&& memcmp(list[i].name, "4a4f4449", 8) != 0 && memcmp(list[i].name, "4A4F4449", 8) != 0
&& memcmp(list[i].name, "af1bf516", 8) != 0 && memcmp(list[i].name, "AF1BF516", 8) != 0
// And ignore everything that's not using characters or numbers(only check 1st char)
&& strtol(list[i].name,NULL,16) >= 0x30000000 && strtol(list[i].name,NULL,16) <= 0x7a000000 ){
			
			int cfd, length;

			u8 *buffer = allocate_memory(0x1E4+4);

			// Try to read from banner.bin first
			sprintf(tempString, "/title/00010001/%s/content/title.tmd", list[i].name);

			cfd = ISFS_Open(tempString, ISFS_OPEN_READ);
			if (cfd>=0){
				ret = ISFS_Read(cfd, buffer, 0x1E4+4);
				if (ret < 0){
					printf("ISFS_Read for %s failed %d\n", tempString, ret);
					ISFS_Close(cfd);
					free(buffer);
					continue;
				}
				ISFS_Close(cfd);	

				//gameList[gameCnt].bannerContent=buffer[0x1E4]*16777216+buffer[0x1E5]*65536+buffer[0x1E6]*256+buffer[0x1E7];
				memcpy(&gameList[gameCnt].bannerContent, buffer+0x1E4, 4);
				free(buffer);

				//try to read banner content
				sprintf(tempString, "/title/00010001/%s/content/%08x.app", list[i].name, gameList[gameCnt].bannerContent);

				cfd = ISFS_Open(tempString, ISFS_OPEN_READ);
				if(cfd<0){
					continue;
				}else{
					buffer=allocate_memory(0x250+4);
					ret = ISFS_Read(cfd, buffer, 0x150);
					if(ret<0){
						printf("ISFS_Read for %s failed %d\n", tempString, ret);
						ISFS_Close(cfd);
						free(buffer);
						continue;
					}
					ISFS_Close(cfd);


					length=0;
					
					for(j=0; buffer[0xF0 + j*2 + 1] != 0x00; j++)
						{
						length++;
						}
					
					// stfour: BEGIN
					if (length >= MAX_TITLE_LEN) length = MAX_TITLE_LEN;

					for(j=0; j<length; j++)
						gameList[gameCnt].title[j] = buffer[0xF0 + j*2 + 1];
					
					gameList[gameCnt].title[j]='\0';
					
					// stfour: END
					
					free(buffer);

					//get ID
					gameList[gameCnt].idInt=TITLE_ID(0x00010001, strtol(list[i].name,NULL,16));

					//get ID string
					memcpy(&gameList[gameCnt].id, (u8*)(&gameList[gameCnt].idInt)+4, 4);
					gameList[gameCnt].id[4]='\0';

					//default options
					gameList[gameCnt].videoMode=0;
					gameList[gameCnt].videoPatch=0;
					gameList[gameCnt].language=0;
					gameList[gameCnt].hooktype=0;
					gameList[gameCnt].ocarina=0;
					gameList[gameCnt].debugger=0;
					gameList[gameCnt].bootMethod=checkApploaderGame(gameList[gameCnt].id);
					gameList[gameCnt].datacleartype=0;

					gameCnt++;
				}
			}
		}
	}
	free(list);
	
	return gameCnt;
}

void __Load_Config(void){
	int ret, i, j, k;
	s16* posiciones=allocate_memory(sizeof(u16)*gameCnt);

	orden=allocate_memory(sizeof(u16)*MAXGAMES);

	for(i=0; i<gameCnt; i++)
		posiciones[i]=-1;

	
	// Ordenar los juegos segun archivo de configuracion
	char *archivoLeido=NULL;

	ret = Fat_ReadFile(MIGHTY_PATH MIGHTY_CONFIG_FILE, (void *)&archivoLeido);

	if(ret>0){
		// Parse config file
		i=16;
		while(i<ret){
			j=archivoLeido[i]+archivoLeido[i+1]*256;

			for(k=0; k<4; k++)
				tempString[k]=archivoLeido[i+2+k];
			tempString[4]='\0';

			for(k=0; k<gameCnt; k++){
				if(strcmp(tempString, gameList[k].id)==0){
					//printf("%s found at %d\n", tempString, k);
					gameList[k].videoMode=archivoLeido[i+6];
					gameList[k].videoPatch=archivoLeido[i+7];
					gameList[k].language=archivoLeido[i+8];
					gameList[k].hooktype=archivoLeido[i+9];
					gameList[k].ocarina=archivoLeido[i+10];
					gameList[k].debugger=archivoLeido[i+11];
					gameList[k].bootMethod=archivoLeido[i+12];
					gameList[k].datacleartype=archivoLeido[i+13];
					//gameList[k].ios=archivoLeido[i+14];

					posiciones[k]=j;
					break;
				}
			}
			i+=16;
		}
		free(archivoLeido);
	}

	//Initialize empty
	for(i=0; i<MAXGAMES; i++)
		orden[i]=EMPTY;

	//Place games
	for(i=0; i<gameCnt; i++)
		if(posiciones[i]!=-1)
			orden[posiciones[i]]=i;

	//Check if some game was not placed
	for(i=0; i<gameCnt; i++){
		if(posiciones[i]==-1){
			for(j=0; j<MAXGAMES; j++){
				if(orden[j]==EMPTY){
					posiciones[i]=j;
					break;
				}
			}
		}
		orden[posiciones[i]]=i;
	}
	free(posiciones);

	selectedGame=0;
	pagina=0;
	calcularPaginas();
}

void __Free_Channel_Images(void){
	int i;
	int imagesPerScreen = COLS[wideScreen]*ROWS;

	for(i=0; i<maxPages*imagesPerScreen; i++){
		//printf("%d: %d\n", i, orden[i]);

		if(!pageLoaded[i/imagesPerScreen]){
			i += imagesPerScreen;
			continue;
		}
		if(orden[i]!=EMPTY){
			MRC_Free_Texture(gameList[orden[i]].banner);
		}
	}

	for (i=0; i<maxPages; i++)
		pageLoaded[i] = FALSE;
	pagina=0;
}

void __Finish_ALL_GFX(void){
	int i;

	__Free_Channel_Images();

	for(i=0; i<MAX_TEXTURES; i++){
		MRC_Free_Texture(textures[i]);
	}
	MRC_Finish();

	haHabidoCambios=false;
}

#define PROJECTION_HEIGHT 64
void __Load_Images_From_Page(void){
	void *imgBuffer=NULL;
	int i, max, pos, ret, game;

	#ifdef DEBUG_MODE
	printf("Loading images...\n");
	#endif

	max=COLS[wideScreen]*ROWS;
	pos=max*pagina;


	for(i=0; i<max; i++){
		game=orden[pos+i];
		if(game!=EMPTY){
			__Draw_Loading();

			// Load image from FAT
			sprintf(tempString, MIGHTY_PATH IMAGES_PREFIX "/%s.png", gameList[game].id);

			ret=Fat_ReadFile(tempString, &imgBuffer);
			// Decode image
			if(ret>0){
				gameList[game].banner=MRC_Load_Texture(imgBuffer);
				free(imgBuffer);
			}else{
				gameList[game].banner=__Create_No_Banner(gameList[game].id, gameList[game].title, ANCHOIMAGEN[wideScreen], ALTOIMAGEN);
			}

            MRC_Resize_Texture(gameList[game].banner, ANCHOIMAGEN[wideScreen], ALTOIMAGEN);
			__MaskBanner(gameList[game].banner); 
			MRC_Center_Texture(gameList[game].banner, 1);
		
			
		}
		//MRC_Draw_Texture(64, 440, configuracionJuegos[game].banner);
	}
	pageLoaded[pagina] = TRUE;
}




void __Load_Skin_From_FAT(void){
	const char* fileNames[MAX_TEXTURES]={
		"arrows", "background", (wideScreen? "container_channel_wide" : "channel_cover"), "empty",
		"loading", "numbers"};
	const u8* defaultTextures[MAX_TEXTURES]={
		mighty_channels_arrows_png, mighty_channels_background_png, (wideScreen? mighty_channels_container_channel_wide_png : mighty_channels_container_channel_png), mighty_channels_empty_png,
		mighty_channels_loading_png, mighty_channels_numbers_png};

	int i, ret;
	char *imgData = NULL;

	for(i=0; i<MAX_TEXTURES; i++){
		sprintf(tempString, MIGHTY_PATH "mighty_channels_%s.png", fileNames[i]);
		ret = Fat_ReadFile(tempString, (void*)&imgData);
		if(ret>0){
			textures[i]=MRC_Load_Texture(imgData);
			free(imgData);
		}else{
			textures[i]=MRC_Load_Texture((void *)defaultTextures[i]);
		}
	}


	//Resize containers if widescreen
	if(wideScreen){
		MRC_Resize_Texture(textures[TEX_EMPTY], ANCHOIMAGEN[1], ALTOIMAGEN);
	}
	__MaskBanner(textures[TEX_EMPTY]);
	MRC_Center_Texture(textures[TEX_ARROWS], 6);
	MRC_Center_Texture(textures[TEX_CONTAINER], 2);
	MRC_Center_Texture(textures[TEX_EMPTY], 1);
}


int __Select_Game(void){
	int i, j, hotSpot, hotSpotPrev, ret;
	ret=MENU_EXIT;

	if(gameCnt==0)
		return MENU_HOME;

	// Create/restore hotspots
	for(i=0;i<ROWS;i++){
		for(j=0;j<COLS[wideScreen];j++){
			int pos=i*COLS[wideScreen]+j;
			Wpad_AddHotSpot(pos,
				FIRSTCOL- (ANCHOIMAGEN[wideScreen] / 2) + (j * SEPARACIONX[wideScreen]),
				FIRSTROW-ALTOIMAGEN/2+i*SEPARACIONY,
				ANCHOIMAGEN[wideScreen],
				ALTOIMAGEN,
				(i==0? COLS[wideScreen]*(ROWS-1)+j : pos-COLS[wideScreen]),
				(i==ROWS-1? j : pos+COLS[wideScreen]),
				(j==0? pos+COLS[wideScreen]-1 : pos-1),
				(j==COLS[wideScreen]-1? pos-COLS[wideScreen]+1 : pos+1)
			);
		}
	}
	Wpad_AddHotSpot(HOTSPOT_LEFT, 0,160,32,88, HOTSPOT_LEFT, HOTSPOT_LEFT, HOTSPOT_RIGHT, 0);
	Wpad_AddHotSpot(HOTSPOT_RIGHT, 640-32,160,32,88, HOTSPOT_RIGHT, HOTSPOT_RIGHT,(COLS[wideScreen]*2)-1, HOTSPOT_LEFT);
	hotSpot=hotSpotPrev=-1;

	// Load images from actual page
	if(!pageLoaded[pagina])
		__Load_Images_From_Page();
	__Draw_Page(-1);


	// Select game loop
	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if(movingGame>-1){
			__Draw_Page(hotSpot);
		}else if(hotSpot!=hotSpotPrev){
			hotSpotPrev=hotSpot;
			__Draw_Page(hotSpot);
		}

		if(hotSpot>=0 && hotSpot<=COLS[wideScreen]*ROWS){
			selectedGame=pagina*COLS[wideScreen]*ROWS+hotSpot;
		}else{
			selectedGame=-1;
		}

		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), (movingGame>-1));

		if(movingGame==-1){
			if(((WPAD_ButtonsDown(WPAD_CHAN_0) & (WPAD_BUTTON_A | WPAD_BUTTON_B | WPAD_BUTTON_1)) || (PAD_ButtonsDown(0) & (PAD_BUTTON_A | PAD_TRIGGER_Z))) && hotSpot>-1 && hotSpot<COLS[wideScreen]*ROWS && orden[selectedGame]!=EMPTY){
				if(WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_B){
					movingGame=selectedGame;
					calcularPaginas();
				}else if((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)){
					ret=MENU_SHOW_GAME;
					break;
				}else if((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_1) || (PAD_ButtonsDown(0) & PAD_BUTTON_Y)){
					ret=MENU_CONFIG_GAME;
					break;
				}
			}
		}else{
			//moving game
			if(!(WPAD_ButtonsHeld(WPAD_CHAN_0) & WPAD_BUTTON_B)){
				if(selectedGame!=-1 && selectedGame!=movingGame){
					u16 copia0=orden[movingGame];
					u16 copia1=orden[selectedGame];

					orden[movingGame]=copia1;
					orden[selectedGame]=copia0;
					haHabidoCambios=true;
				}
				movingGame=-1;
				calcularPaginas();
				__Draw_Page(hotSpot);
			}
		}

		if(WPAD_ButtonsDown(WPAD_CHAN_0) || PAD_ButtonsDown(0)){
			if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_MINUS) || (PAD_ButtonsDown(0) & PAD_TRIGGER_L)) || (hotSpot==HOTSPOT_LEFT && ((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)))){
				if (pagina==0)
					pagina = maxPages;
				pagina--;
				ret=MENU_SELECT_GAME;
				break;
			}else if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_PLUS) || (PAD_ButtonsDown(0) & PAD_TRIGGER_R)) || (hotSpot==HOTSPOT_RIGHT && ((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)))){
				pagina++;
				if (pagina>=maxPages)
					pagina = 0;
				ret=MENU_SELECT_GAME;
				break;
			}else if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_HOME) || (PAD_ButtonsDown(0) & PAD_BUTTON_START))){
				ret=MENU_HOME;
				break;
			}if(gameCnt>2 && ((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_2) || (PAD_ButtonsDown(0) & PAD_TRIGGER_Z))){
				ret=MENU_SORT_GAMES;
				break;
			}

			//if(WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_2)
			//	MRC_Capture();

		}

	}

	return ret;
}


#define CONFIGITEMS 8
int __Config_Game(void){
	int i, hotSpot, hotSpotPrev;
	bool repaint=true;

	//Obtener informacion del juego
	title *config=&gameList[orden[selectedGame]];
	char* gameId=config->id;

	u32 optioncount[CONFIGITEMS]={8,6,11,8,4,2,2,2};
	u32 optionselected[CONFIGITEMS] = {config->videoMode, config->videoPatch, config->language, config->hooktype, config->ocarina, config->debugger, config->bootMethod, config->datacleartype};
	char *videooptions[8]={"Default Video Mode", "Force NTSC480i", "Force NTSC480p", "Force PAL480i", "Force PAL480p", "Force PAL576i", "Force MPAL480i", "Force MPAL480p"};
	char *videopatchoptions[6]={"No Video patches", "Sneek's Video patch", "Smart Video patching", "More Video patching", "Full Video patching", "Sneek + Full patching" };
	char *languageoptions[11]={"Default Language", "Japanese", "English", "German", "French", "Spanish", "Italian", "Dutch", "S. Chinese", "T. Chinese", "Korean"};
	char *hooktypeoptions[8]={"No Ocarina&debugger", "Hooktype: VBI", "Hooktype: KPAD", "Hooktype: Joypad", "Hooktype: GXDraw", "Hooktype: GXFlush", "Hooktype: OSSleepThread", "Hooktype: AXNextFrame"};
	char *ocarinaoptions[4]={"No Ocarina", "Ocarina from NAND", "Ocarina from SD", "Ocarina from USB"};
	char *debuggeroptions[2]={"No debugger", "Debugger enabled"};
	char *bootmethodoptions[2]={"Normal boot method", "Load apploader"};
	char *cleardataoptions[2]={"No Data Clear", "Clear Data First"};

	__Draw_Window(600, 380, lang[15]);
	MRC_Draw_Tile(160, 224, textures[TEX_CONTAINER], textures[TEX_CONTAINER]->width/2, 0);
	MRC_Draw_Texture(160, 224, config->banner);
	MRC_Draw_String(140, 300, 0x505050ff, gameId);


	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<CONFIGITEMS;i++){
		Wpad_AddHotSpot(i,
			350,
			80+i*42,
			200,
			32,
			(i==0? CONFIGITEMS-1 : i-1),
			(i==CONFIGITEMS-1? 0 : i+1),
			(i==0? 15 : 20), (i==0? 15 : 20)
		);
	}
	Wpad_AddHotSpot(15,
		150, 112,
		50, 32,
		20, 20,
		0, 0
	);
	MRC_Draw_String(50, 80, BLACK, lang[16]);
	Wpad_AddHotSpot(20,
		64, 368,
		200, 32,
		15, 15,
		6, 6
	);

	// Loop
	hotSpot=hotSpotPrev=-1;

	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if(hotSpot!=hotSpotPrev || repaint){
			hotSpotPrev=hotSpot;

			__Draw_Button(0, videooptions[optionselected[0]], hotSpot==0);
			__Draw_Button(1, videopatchoptions[optionselected[1]], hotSpot==1);
			__Draw_Button(2, languageoptions[optionselected[2]], hotSpot==2);
			__Draw_Button(3, hooktypeoptions[optionselected[3]], hotSpot==3);
			__Draw_Button(4, ocarinaoptions[optionselected[4]], hotSpot==4);
			__Draw_Button(5, debuggeroptions[optionselected[5]], hotSpot==5);
			__Draw_Button(6, bootmethodoptions[optionselected[6]], hotSpot==6);
			__Draw_Button(7, cleardataoptions[optionselected[7]], hotSpot==7);

			__Draw_Button(15, (haHabidoCambios? lang[18]:lang[21]), hotSpot==15);
			__Draw_Button(20, lang[0], hotSpot==20);
			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)) && hotSpot>-1){
			if(hotSpot<CONFIGITEMS){
				optionselected[hotSpot]++;
				if(optionselected[hotSpot]==optioncount[hotSpot])
					optionselected[hotSpot]=0;
				repaint=true;
			}else if(hotSpot==15){
				haHabidoCambios=!haHabidoCambios;
				repaint=true;
			}else if(hotSpot==20){
				break;
			}
		}else if((WPAD_ButtonsDown(WPAD_CHAN_0) & (WPAD_BUTTON_HOME | WPAD_BUTTON_B | WPAD_BUTTON_1)) || (PAD_ButtonsDown(0) & (PAD_BUTTON_START | PAD_BUTTON_B | PAD_TRIGGER_Z))){
			break;
		}
	}

	config->videoMode=optionselected[0];
	config->videoPatch=optionselected[1];
	config->language=optionselected[2];
	config->hooktype=optionselected[3];
	config->ocarina=optionselected[4];
	config->debugger=optionselected[5];
	config->bootMethod=optionselected[6];
	config->datacleartype=optionselected[7];

	return MENU_SELECT_GAME;
}


int __Sort_Games(void){
	int i, j, k, hotSpot, hotSpotPrev;
	int nSystems=0;
	int selectedSystem=-1;
	int gamesPerPage=COLS[wideScreen]*ROWS;
	unsigned char systems[16];
	bool repaint=true;

	/*//Get all systems
	for(i=0; i<gameCnt && nSystems<16; i++){
		for(j=0; j<nSystems; j++)
			if(systems[j]==gameList[i].id[0])
				break;

		if(j==nSystems){
			systems[nSystems]=gameList[i].id[0];
			nSystems++;
		}
	}*/

	//Get all systems
	for(i=0; i<MAXGAMES && nSystems<16; i++){
		if(orden[i]!=EMPTY){
			for(j=0; j<nSystems; j++)
				if(systems[j]==gameList[orden[i]].id[0])
					break;
	
			if(j==nSystems){
				systems[nSystems]=gameList[orden[i]].id[0];
				nSystems++;
			}
		}
	}


	__Draw_Window(600, 120, lang[4]);


	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<nSystems;i++){
		Wpad_AddHotSpot(i,
			32+i*48, 200,
			40, 32,
			20, 20,
			(i==0? nSystems-1 : i-1), (i==nSystems-1? 0 : i+1)
		);
	}

	Wpad_AddHotSpot(20,
		64, 256,
		200, 32,
		0, 0,
		21, 21
	);
	Wpad_AddHotSpot(21,
		364, 256,
		200, 32,
		0, 0,
		20, 20
	);

	// Loop
	hotSpot=hotSpotPrev=-1;

	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if(hotSpot!=hotSpotPrev || repaint){
			hotSpotPrev=hotSpot;

			for(i=0; i<nSystems; i++){
				if(i==selectedSystem){
					sprintf(tempString, "<%c>", systems[i]);
				}else{
					sprintf(tempString, "%c", systems[i]);
				}
				__Draw_Button(i, tempString, selectedSystem==i || hotSpot==i);
			}

			__Draw_Button(20, lang[0], hotSpot==20);
			__Draw_Button(21, lang[4], hotSpot==21);
			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)) && hotSpot>-1){
			if(hotSpot<nSystems){
				if(selectedSystem==-1){
					selectedSystem=hotSpot;
				}else{
					unsigned char copia0=systems[selectedSystem];
					unsigned char copia1=systems[hotSpot];

					systems[selectedSystem]=copia1;
					systems[hotSpot]=copia0;
					selectedSystem=-1;
				}

				repaint=true;
			}else if(hotSpot==20){
				break;
			}else if(hotSpot==21){
				__Free_Channel_Images();
				for(i=0; i<gameCnt; i++){
					orden[i]=EMPTY;
				}
				k=0;
				for(i=0; i<nSystems; i++){
					for(j=0; j<gameCnt; j++){
						if(gameList[j].id[0]==systems[i]){
							orden[k]=j;
							k++;
						}
					}

					while(k%gamesPerPage!=0){
						k++;
					}
				}
				haHabidoCambios=true;
				calcularPaginas();
				break;
			}
		}else if((WPAD_ButtonsDown(WPAD_CHAN_0) & (WPAD_BUTTON_HOME | WPAD_BUTTON_B | WPAD_BUTTON_1)) || (PAD_ButtonsDown(0) & (PAD_BUTTON_START | PAD_BUTTON_B | PAD_TRIGGER_Z))){
			break;
		}
	}


	return MENU_SELECT_GAME;
}

#define PROJECTION_HEIGHT 64
int __Show_Game(void){
	void* imageBuffer;
	MRCtex *gameImage, *projection;
	int i, j, ret;
	char *c, *r, a;
	title* game=&gameList[orden[selectedGame]];

	SoundInfo snd;

	
	// BLACK SCREEN
	a=160;
	for(i=0; i<480; i++){
		if(a<255 && ((i<208 && i%4==0) || i%8==0))
			a++;
		MRC_Draw_Box(0, i, 640, 1, a);
	}

	/*
	// ANOTHER SCREEN FADE TYPE
	a=200;
	for(i=0; i<480; i++){
		if(a<255 && ((i<100 && i%4==0) || (i>200 && i%8==0)))
			a++;
		MRC_Draw_Box(0, i, 640, 1, 0x20202000+a);
	}*/


	// Load image from FAT
	sprintf(tempString, MIGHTY_PATH IMAGES_PREFIX "/%s.png", game->id);

	ret=Fat_ReadFile(tempString, &imageBuffer);
	// Decode image
	if(ret>0){
		gameImage=MRC_Load_Texture(imageBuffer);
		free(imageBuffer);

		MRC_Resize_Texture(gameImage, (wideScreen? 168 : 224),308);
		//__MaskBanner(gameImage);
		MRC_Center_Texture(gameImage, 1);

		projection=allocate_memory(sizeof(MRCtex));
		projection->buffer=allocate_memory(gameImage->width*PROJECTION_HEIGHT*4);
		projection->width=gameImage->width;
		projection->height=PROJECTION_HEIGHT;
		MRC_Center_Texture(projection, 1);
		projection->alpha=true;
	
		a=128;
		r=(projection->buffer);
		for(i=0; i<PROJECTION_HEIGHT; i++){
			c=(gameImage->buffer)+(((gameImage->height-1)-i*2)*gameImage->width)*4;
			for(j=0; j<gameImage->width; j++){
				r[0]=c[0];
				r[1]=c[1];
				r[2]=c[2];
				r[3]=a;
				c+=4;
				r+=4;
			}
			if(a>4)
				a-=4;
		}
	
		MRC_Draw_String((640-strlen(game->title)*8)/2+2, 420, 0x00000000, game->title);
		MRC_Draw_String((640-strlen(game->title)*8)/2, 420, 0xFFFFFF00, game->title);
		MRC_Draw_Texture(320, 228, gameImage);
		MRC_Draw_Texture(320, 416, projection);
		MRC_Free_Texture(gameImage);
		MRC_Free_Texture(projection);
	}else{
		MRC_Draw_String((640-strlen(game->title)*8)/2+2, 220, 0x00000000, game->title);
		MRC_Draw_String((640-strlen(game->title)*8)/2, 220, 0xFFFFFF00, game->title);
	}
	MRC_Render_Screen();



	//SONIDO BANNER
	//reproducir sonido banner
	ASND_Init();
	ASND_Pause(0);
	
	memset(&snd, 0, sizeof(snd));
	//WBFS_BannerSound(header->id, &snd);

	if(nandMode==0){
		u32 tempSize=0;
		if(ISFS_Initialize()==0){
			sprintf(tempString, "/title/00010001/%08x/content/%08x.app", TITLE_LOWER(game->idInt), game->bannerContent);
			ret=read_full_file_from_nand(tempString, (u8**)&imageBuffer, &tempSize);
			ISFS_Deinitialize();
			if(ret==0 && tempSize)
				parse_banner_snd(imageBuffer, &snd);
		}
	}else{
		sprintf(tempString, "fat:/%s/title/00010001/%08x/content/%08x.app", Get_Path(), TITLE_LOWER(game->idInt), game->bannerContent);
		ret=Fat_ReadFile(tempString, &imageBuffer);
		if(ret)
			parse_banner_snd(imageBuffer, &snd);
	}

	if (snd.dsp_data) {
		int fmt = (snd.channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT;

		if(snd.loop){
			ASND_SetInfiniteVoice(0, fmt, snd.rate, 0,			
				snd.dsp_data, snd.size,
				160,160); //volumen,volumen
		}else{
			SND_SetVoice(0, fmt, snd.rate, 0,
				snd.dsp_data, snd.size,
				96,96, //volumen,volumen,
				NULL); //DataTransferCallback
		}
	}

	ret=MENU_SELECT_GAME;
	for(;;){
		WPAD_ScanPads();
		PAD_ScanPads();
		if(WPAD_ButtonsDown(WPAD_CHAN_0) || PAD_ButtonsDown(0)){
			if((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)){
				ret=MENU_START_GAME;
			}
			break;
		}
	}

	// Stop banner sound
	if(snd.dsp_data){
		SND_StopVoice(0);
		ASND_End();

		free(snd.dsp_data);
		snd.dsp_data=NULL;
	}
	if(imageBuffer){
		//__Draw_Message("FREE IMAGE BUFFER", 1);
		free(imageBuffer);
	}

	if(ret==MENU_START_GAME){
		MRC_Draw_String(32, 420, WHITE, lang[23]);
		MRC_Render_Box(32, 420);
	}

	return ret;
}


#define HOME_BUTTON_X			208
#define HOME_BUTTON_Y			120
#define HOME_BUTTON_WIDTH		224
#define HOME_BUTTON_HEIGHT		56
#define HOME_BUTTON_SEPARATION	4
int __Home(void){
	int i, hotSpot, hotSpotPrev, ret;
	bool repaint=true;

	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<4; i++){
		Wpad_AddHotSpot(i,
			HOME_BUTTON_X,
			HOME_BUTTON_Y+i*(HOME_BUTTON_HEIGHT+HOME_BUTTON_SEPARATION),
			HOME_BUTTON_WIDTH,
			HOME_BUTTON_HEIGHT,
			(i==0? 3 : i-1),
			(i==3? 0 : i+1),
			i, i
		);
	}


	__Draw_Window(HOME_BUTTON_WIDTH+44, 320, "Mighty Channels MOD v11.1");

	sprintf(tempString, "IOS %d v %d", IOS_GetVersion(), IOS_GetRevision());
	MRC_Draw_String(10, 420, 0x505050ff, tempString);
	MRC_Draw_String(10, 440, 0x505050ff, lang[9+nandMode]);
	sprintf(tempString, "by Marc R./xFede");
	MRC_Draw_String(240, 372, 0xcacacaff, tempString);

	// Loop
	hotSpot=hotSpotPrev=-1;

	ret=MENU_SELECT_GAME;
	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if((hotSpot!=hotSpotPrev && hotSpot<4) || repaint){
			hotSpotPrev=hotSpot;

			for(i=0; i<4; i++){
				__Draw_Button(i, lang[i], hotSpot==i);
			}
			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)) && hotSpot>-1 && hotSpot<4){
			if(hotSpot==1)
				ret=MENU_MANAGE;
			else if(hotSpot==2)
				ret=MENU_DOWNLOAD;
			else if(hotSpot==3)
				ret=MENU_EXIT;
			break;
		}else if(((WPAD_ButtonsDown(WPAD_CHAN_0) & (WPAD_BUTTON_HOME | WPAD_BUTTON_B)) || (PAD_ButtonsDown(0) & (PAD_BUTTON_START | PAD_BUTTON_B)))){
			break;
		}
	}

	return ret;
}

int __Update_File_From_Real_Nand(char* filepath){
	u8 *inBuffer = NULL;
	u32 inSize;
	int ret=0;

	//ISFS init
	ret=ISFS_Initialize();
	if(ret<0){
		__Draw_Message(lang[35], 0);
		return ret;
	}

	ret=read_full_file_from_nand(filepath, &inBuffer, &inSize);
	ISFS_Deinitialize();
	if(ret<0){
	sprintf(tempString, lang[36], filepath);
		__Draw_Message(tempString, 0); //0 or ret?
		return ret;
	}

	sprintf(tempString, "fat:/%s/%s", Get_Path(), filepath);
	ret=Fat_SaveFile(tempString, (void *)&inBuffer, inSize);
	if(ret<0)
		__Draw_Message(lang[37], 0);

	return ret;
}

int __Manage_Nand(void){
	// "fat:/ticket/00000001/0000..." Fix return to wii menu
	// "/shared2/sys/SYSCONF" Fix WiiMote Sync Issues
	// "/shared2/menu/FaceLib/RFL_DB.dat" update Miis

	int i, hotSpot, hotSpotPrev;
	char origTicket[128];
	char fakeTicket[128];
	bool repaint=true;


	if(nandMode==REAL_NAND)
		return MENU_HOME;

	__Draw_Window(600, 380, lang[1]);


	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<3;i++){
		Wpad_AddHotSpot(i,
			64,
			80+i*96,
			500,
			56,
			(i==0? 3 : i-1),
			i+1,
			i, i
		);
	}

	Wpad_AddHotSpot(3,
		64, 368,
		200, 32,
		2, 0,
		3, 3
	);

	// Loop
	hotSpot=hotSpotPrev=-1;

	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if(hotSpot!=hotSpotPrev || repaint){
			hotSpotPrev=hotSpot;

			__Draw_Button(0, lang[25], hotSpot==0);
			__Draw_Button(1, lang[26], hotSpot==1);
			__Draw_Button(2, lang[27], hotSpot==2);

			__Draw_Button(3, lang[0], hotSpot==3);
			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A)) && hotSpot>-1){
			if(hotSpot==0){
				sprintf(origTicket, "fat:/%s/ticket/00000001/00000002.tik", Get_Path());
				sprintf(fakeTicket, "fat:/%s/ticket/00000001/_00000002.tik", Get_Path());

				if(Fat_CheckFile(origTicket)){
					if(__Question_Window(lang[1], lang[38], lang[19], lang[18])){
						if(rename(origTicket, fakeTicket)==0){
							__Draw_Message(lang[39], 0);
							__Draw_Message(lang[40], 0);
						}else{
							__Draw_Message(lang[41], 0);
						}
					}
				}else{
					if(__Question_Window(lang[1], lang[42], lang[19], lang[18])){
						if(rename(fakeTicket, origTicket)==0){
							__Draw_Message(lang[43], 0);
							__Draw_Message(lang[44], 0);
						}else{
							__Draw_Message(lang[45], 0);
						}
					}
				}
				break;
			}else if(hotSpot==1){
				if(__Question_Window(lang[1], lang[46], lang[19], lang[18])){
					__Update_File_From_Real_Nand("/shared2/sys/SYSCONF");
				}
				break;
			}else if(hotSpot==2){
				if(__Question_Window(lang[1], lang[47], lang[19], lang[18])){
					__Update_File_From_Real_Nand("/shared2/menu/FaceLib/RFL_DB.dat");
				}
				break;
			}else if(hotSpot==3){
				break;
			}
		}else if((WPAD_ButtonsDown(WPAD_CHAN_0) & (WPAD_BUTTON_HOME | WPAD_BUTTON_B)) || (PAD_ButtonsDown(0) & (PAD_BUTTON_START | PAD_BUTTON_B))){
			break;
		}
	}

	return MENU_HOME;
}

int __Select_Nand_Mode(void){
	int i, hotSpot, hotSpotPrev, ret;
	bool repaint=true;

	// Create/restore hotspots
	Wpad_CleanHotSpots();
	for(i=0; i<3; i++){
		Wpad_AddHotSpot(i,
			HOME_BUTTON_X,
			160+i*(HOME_BUTTON_HEIGHT+HOME_BUTTON_SEPARATION),
			HOME_BUTTON_WIDTH,
			HOME_BUTTON_HEIGHT,
			(i==0? 2 : i-1),
			(i==2? 0 : i+1),
			i, i
		);
	}

	// Background
	MRC_Draw_Texture(0, 0, textures[TEX_BACKGROUND]);

	MRC_Draw_String(128, HOME_BUTTON_Y, BLACK, lang[8]);

	sprintf(tempString, "IOS%d v%d", IOS_GetVersion(), IOS_GetRevision());
	MRC_Draw_String(10, 440, 0x505050ff, tempString);

	// Loop
	hotSpot=hotSpotPrev=-1;

	ret=0;
	for(;;){
		hotSpot=Wpad_Scan();

		// If hot spot changed
		if((hotSpot!=hotSpotPrev && hotSpot<4) || repaint){
			hotSpotPrev=hotSpot;

			for(i=0; i<3; i++){
				__Draw_Button(i, lang[9+i], hotSpot==i);
			}
			repaint=false;
		}
		MRC_Draw_Cursor(Wpad_GetWiimoteX(), Wpad_GetWiimoteY(), 0);

		if(((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_A) || (PAD_ButtonsDown(0) & PAD_BUTTON_A))){
			if(hotSpot==0)
				ret=REAL_NAND;
			else if(hotSpot==1)
				ret=EMU_SD;
			else if(hotSpot==2)
				ret=EMU_USB;
			break;
		}
	}

	return ret;
}



#define DOWNLOAD_WINDOW_X		52
#define DOWNLOAD_WINDOW_Y		180
#define DOWNLOAD_WINDOW_WIDTH	536
#define DOWNLOAD_WINDOW_HEIGHT	112
int __Download(void){
	int i, j, ret;
	int downloadedImages=0;
	char myId[8];
	char fatPath[128];

	int downloadPreference[4];
	const char *regions[4]={"EN", "US", "JA", "other" };
	const char regionChars[4]="PEJ?";

	if(gameCnt==0)
		return MENU_HOME;

	Fat_MakeDir(MIGHTY_PATH IMAGES_PREFIX);


	__Draw_Message(lang[5], 0);
	

	for(;;){
		ret=net_init();
		if(ret<0 && ret!=-EAGAIN){
			//net_deinit();
			__Draw_Message(lang[33], 0);

			return MENU_HOME;
		}
		if(ret==0) //consigo conexion
			break;
		sleep(5);
	}

   	Wpad_Scan();
	__Draw_Message(lang[6], 0);
	
	for(i=0;i<gameCnt;i++){
	sprintf(fatPath, MIGHTY_PATH IMAGES_PREFIX "/%s.png", gameList[i].id);
		if(!Fat_CheckFile(fatPath)){
			__Draw_Loading();
			if(gameList[i].id[3]=='P'){
				downloadPreference[0]=0;
				downloadPreference[1]=1;
				downloadPreference[2]=2;
				downloadPreference[3]=3;
			}else if(gameList[i].id[3]=='E'){
				downloadPreference[0]=1;
				downloadPreference[1]=0;
				downloadPreference[2]=2;
				downloadPreference[3]=3;
			}else if(gameList[i].id[3]=='J'){
				downloadPreference[0]=2;
				downloadPreference[1]=0;
				downloadPreference[2]=1;
				downloadPreference[3]=3;
			}else{
				downloadPreference[0]=3;
				downloadPreference[1]=0;
				downloadPreference[2]=1;
				downloadPreference[3]=2;
			}
			for(j=0; j<8; j++){
				//If region=='other' get original region ID
				myId[0]=gameList[i].id[0];
				myId[1]=gameList[i].id[1];
				myId[2]=gameList[i].id[2];
				if(regionChars[downloadPreference[j/2]]=='?')
					myId[3]=gameList[i].id[3];
				else
					myId[3]=regionChars[downloadPreference[j/2]];
				myId[4]=0;
					
				sprintf(tempString, "http://wiitdb.com/wiitdb/artwork/cover/%s/%s.png", regions[downloadPreference[j/2]], myId);

				//__Draw_Message(tempString, -1);
					            

				u8* outbuf=NULL;
				u32 outlen=0;
				u32 http_status=0;
	
                
				ret=http_request(tempString, 1 << 31);
				if(!ret){
					continue;
				}

				ret = http_get_result(&http_status, &outbuf, &outlen);
				
				if(outlen>64){//suficientes bytes
				if((WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_B) || (PAD_ButtonsDown(0) & PAD_BUTTON_B))
	            {__Draw_Message(lang[34], 0);				
	            goto interrompi;
	            }
				else
				Fat_SaveFile(fatPath, (void *)&outbuf,outlen);
				}

				if(outbuf!=NULL)
				free(outbuf);

				if(outlen>64){//suficientes bytes
					downloadedImages++;
					break;
				}
				
			}
		}
	}
	
interrompi:	

	net_deinit();

	if(downloadedImages==0)
		__Draw_Message(lang[24], 1);
	else
		__Free_Channel_Images();

	return MENU_HOME;
}


int Menu_Loop(int ios2, int autoMode){
	
	int ret=0;
	s32 ios = IOS_GetVersion();

	// Initialize console
	#ifdef DEBUG_MODE
	Con_Init(CONSOLE_X, CONSOLE_Y, CONSOLE_WIDTH, CONSOLE_HEIGHT);
	printf("Mighty Channels TEST\n");	
	#endif

	// Check console language
	ret=CONF_GetLanguage();
	if(ret>0 && ret<=CONF_LANG_DUTCH)
		lang=texts[ret-1];
	else
		lang=texts[0];

	// Check if widescreen
	if(CONF_GetAspectRatio()==CONF_ASPECT_16_9)
		wideScreen=true;
	//wideScreen=false;

	// Init MRC graphics
	MRC_Init();
	textures[1]=MRC_Load_Texture((void *)mighty_channels_background_png);
	
	//Select NAND mode
	if(autoMode==-1)
		nandMode=__Select_Nand_Mode();
	else
		nandMode=autoMode;
		
	__Draw_Message(lang[48], 0);
    
	//Check if the cIOS is a d2x
	if(IsD2X(ios))
	{
	goto prosegui;
	}
	else if(IsHermes(ios))
	{
	__Draw_Message(lang[49], 0);
	goto out;
	}
	else if(IsWaninkoko(ios))
	{
	__Draw_Message(lang[50], 0);
	goto out;
	}
	else
	{
	__Draw_Message(lang[51], 0);
	goto out;
	}
	
	prosegui:

	// Mount fat
	if(nandMode==EMU_USB)
		ret = Fat_Mount(USB);
	else
		ret = Fat_Mount(SD);

	if (ret < 0)
		{
		__Draw_Message(lang[29], 0);
		goto out;
		}

	// Create needed directories
	Fat_MakeDir("fat:/config");
	Fat_MakeDir("fat:/config/mighty");

	__Draw_Message(lang[28], 0);
	__ReadCache ();
	
	Fat_Unmount ();

	//Initialize NAND
	__Draw_Message(lang[12+nandMode], 0);
	if(nandMode!=REAL_NAND){
		ret=Enable_Emu(nandMode);
		if(ret<0){
			__Draw_Message(lang[30], 0);
			goto out;
		}
	}

	ISFS_Initialize();		
	ret=__Get_Entries();
	if(ret==0){
		__Draw_Message(lang[31], 0);
		if(nandMode!=REAL_NAND)
			Disable_Emu();
		goto out;
	}else if(ret<0){
		__Draw_Message(lang[32], 0);
		if(nandMode!=REAL_NAND)
			Disable_Emu();
		goto out;
	}
	ISFS_Deinitialize();
	if(nandMode!=REAL_NAND)
		Disable_Emu();

	// Mount fat
	if(nandMode==EMU_USB)
		ret = Fat_Mount(USB);
	else
		ret = Fat_Mount(SD);
		
	// Dump gameList strutcture to cache file
	__WriteCache();
	
	// Load skin images
	MRC_Free_Texture(textures[1]);
	__Load_Skin_From_FAT();

	// Load config file
	__Load_Config();


	ret=MENU_SELECT_GAME;
	for(;;){
		if(ret==MENU_SELECT_GAME)
			ret=__Select_Game();
		else if(ret==MENU_CONFIG_GAME)
			ret=__Config_Game();
		else if(ret==MENU_SORT_GAMES)
			ret=__Sort_Games();
		else if(ret==MENU_SHOW_GAME)
			ret=__Show_Game();
		else if(ret==MENU_HOME)
			ret=__Home();
		else if(ret==MENU_MANAGE)
			ret=__Manage_Nand();
		else if(ret==MENU_DOWNLOAD)
			ret=__Download();
		else
			break;
	}


	if(haHabidoCambios)
		__Save_Changes();

	__Finish_ALL_GFX();

	if(ret==MENU_START_GAME)
		__Start_Game();

out:
	#ifdef DEBUG_MODE
	printf("Exiting...\n");		
	#endif

	if(!__Check_HBC())
		SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);

	return 0;
}
